#include	"Cache.h"
#include	<Json.h>

bool Cache::Initialize(const std::string & redis_host, int redis_port) {
	_redis.SetLogHandler([](const std::string & msg) { LOG_INFO("%s", msg.c_str()); });
	return _redis.Connect(redis_host, redis_port);
}

bool Cache::Save(uint64_t id, const google::protobuf::Message & data) {
	const google::protobuf::Descriptor * desc = data.GetDescriptor();
	std::string cache_key = desc->name() + "_" + std::to_string(id);
	std::string buffer = data.SerializeAsString();

	return _redis.Command([=](Redis::Reply & reply) {
		if (!reply) LOG_ERR("Cache::Save failed. Key : %s", cache_key.c_str());
	}, "SET %s %b", cache_key.c_str(), buffer.data(), buffer.size());
}

bool Cache::Flush(uint64_t id, const google::protobuf::Message & data) {
	const google::protobuf::Descriptor * desc = data.GetDescriptor();
	std::string cache_key = desc->name() + "_" + std::to_string(id);
	std::string buffer = data.SerializeAsString();
	std::string type = desc->full_name();

	return _redis.Command([=](Redis::Reply & reply) {
		if (!reply) {
			LOG_ERR("Cache::Flush[INSERT] failed. Key : %s", cache_key.c_str());
		} else {
			Json::Value task;
			task["type"] = type;
			task["redis_key"] = cache_key;
			task["is_new"] = true;

			bool success = _redis.Command([=](Redis::Reply & reply) {
				if (!reply) LOG_ERR("Cache::Flush[INSERT] failed. RPUSH flush_task : %s. Error : %s", cache_key.c_str(), reply.AsError());
			}, "RPUSH flush_tasks %s", task.toStyledString().c_str());

			if (!success)
				LOG_ERR("Cache::Flush[INSERT] failed. RPUSH flush_task : %s", task.toStyledString().c_str());
		}
	}, "SET %s %b", cache_key.c_str(), buffer.data(), buffer.size());
}

bool Cache::Flush(uint64_t id, const google::protobuf::Message & data, const std::string & primary) {
	const google::protobuf::Descriptor * desc = data.GetDescriptor();
	std::string cache_key = desc->name() + "_" + std::to_string(id);
	std::string buffer = data.SerializeAsString();
	std::string type = desc->full_name();

	const google::protobuf::FieldDescriptor * field = desc->FindFieldByName(primary);
	if (!field) {
		LOG_ERR("Cache::Flush[UPDATE] failed. Invalid key : %s for %s", primary.c_str(), type.c_str());
		return false;
	}

	return _redis.Command([=](Redis::Reply & reply) {
		if (!reply) {
			LOG_ERR("Cache::Flush[UPDATE] failed. Key : %s", cache_key.c_str());
		} else {
			Json::Value task;
			task["type"] = type;
			task["redis_key"] = cache_key;
			task["is_new"] = false;
			task["db_key"] = primary;

			bool success = _redis.Command([=](Redis::Reply & reply) {
				if (!reply) LOG_ERR("Cache::Flush[UPDATE] failed. RPUSH flush_task : %s. Error : %s", cache_key.c_str(), reply.AsError());
			}, "RPUSH flush_tasks %s", task.toStyledString().c_str());

			if (!success)
				LOG_ERR("Cache::Flush[UPDATE] failed. RPUSH flush_task : %s", task.toStyledString().c_str());
		}
	}, "SET %s %b", cache_key.c_str(), buffer.data(), buffer.size());
}

void Cache::Breath() {
	_redis.Breath();
}